package com.furioussnails.sourcecodeanalysis.sqlsession;

import com.furioussnails.sourcecodeanalysis.pojo.Configuration;
import com.furioussnails.sourcecodeanalysis.pojo.MapperStatement;

import java.beans.IntrospectionException;
import java.lang.reflect.*;
import java.sql.SQLException;
import java.util.List;

public class DefaultSqlSession implements SqlSession {

    private Configuration configuration;

    public DefaultSqlSession(Configuration configuration) {
        this.configuration = configuration;
    }

    public <T> List<T> selectList(String statementId, Object... params) throws SQLException, IllegalAccessException, IntrospectionException, InstantiationException, NoSuchFieldException, InvocationTargetException, ClassNotFoundException {
        SimpleExecutor simpleExecutor = new SimpleExecutor();
        MapperStatement mapperStatement = configuration.getMapperStatementMap().get(statementId);
        List<T> list = simpleExecutor.query(configuration, mapperStatement, params);
        return list;
    }

    public <T> T selectOne(String statement, Object... params) throws Exception {

        List<T> tList = selectList(statement, params);
        if (tList.size() == 1){
            return tList.get(0);
        }else {
            throw new Exception("查询结果为null,或者返回结果过多");
        }
    }

    @Override
    public <T> T getMapper(Class<?> mapperClass) {
        // 使用JDK动态代理来为Dao接口生产代理对象并返回
        T proxyInstance = (T) Proxy.newProxyInstance(DefaultSqlSession.class.getClassLoader(), new Class[]{mapperClass}, (proxy, method, args) -> {
            // 底层都是执行jdbc代码
            // 根据不同情况调用不同的方法
            // 准备参数  1.statementId  sql语句的唯一标识 namespace.id=接口全限定名.方法名
            String name = method.getName();
            String className = method.getDeclaringClass().getName();
            String statementId = className + "." + name;
            // 判断是否进行了 泛型类型参数化
            Type genericReturnType = method.getGenericReturnType();
            if (genericReturnType instanceof ParameterizedType) {
                List<Object> objects = selectList(statementId, args);
                return objects;
            }
            return selectOne(statementId, args);
        });
        return proxyInstance;
    }
}
